# import numpy as np
# from PIL import ImageGrab
from datetime import datetime
import threading
# from typing import Callable
import random
import areaposition
import calculate_discard
# import model
import mjtools
from areaposition import AreaPosition
import cv2
import pyautogui
import time
import os
from pynput import keyboard  # 用于监听键盘事件
import psutil

loop_running = False
main_running = True
# 保存当前键盘监听线程（用于停止旧线程）
keyboard_listener = None
next_game_flag = True
Exit_flag = False
pyautogui.FAILSAFE = False
delay_exit_time = None  # 记录按下a键的时间（用于2小时后退出）


class Monitor:
    def __init__(self, interval: float = 0.5):
        """
        初始化监控器
        :param interval: 检查间隔时间(秒)
        """
        self.interval = interval
        self.running = False
        self.thread = None

    def start(self) -> None:
        """启动监控线程"""
        global loop_running, keyboard_listener
        loop_running = True

        # # 启动新的键盘监听（先停止旧的）
        # stop_keyboard_listener()
        # keyboard_listener = start_keyboard_listener()

        self.thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self.thread.start()
        print("监控已启动")

    def stop(self) -> None:
        """停止监控线程"""
        global loop_running
        loop_running = False
        if self.thread:
            self.thread.join()
        print("监控已停止")

    # 循环主体，调用_check_conditions进行判断
    def _monitor_loop(self) -> None:
        """监控主循环"""
        global loop_running, next_game_flag, delay_exit_time

        while loop_running:
            now = datetime.now()
            timestamp = now.strftime("%Y%m%d_%H%M%S") + f"_{now.microsecond // 1000:03d}"
            print(f"\n以下运行时间为：{timestamp}")
            self._check_conditions()
            time.sleep(self.interval)

            # 检查：如果按下过a键，且已超过2小时（7200秒）
            if delay_exit_time is not None and next_game_flag:
                current_time = time.time()
                if current_time - delay_exit_time >= 10800:  # 2小时=7200秒
                    next_game_flag = False  # 退出监控循环
                    print()
                    print("3小时已到，自动停止监控循环，下一局不会开始...")
                    print()

    # 每次循环的判断
    def _check_conditions(self) -> None:
        global loop_running
        global main_running

        time.sleep(0.1 * random.randint(1, 5))
        """检查所有条件并执行相应操作"""
        # 2. 检测到再来一场确认按钮时，触发点击
        if areaposition.check_next_game_confirm():
            areaposition.click_next_game_confirm()
            return

        # 2. 检测到再来一场按钮时，触发点击
        if areaposition.check_next_game():
            screen_width, screen_height = pyautogui.size()
            # 截取屏幕（会自动保存）
            # screen_img = mjtools.capture_save(AreaPosition("", 0, screen_width, 0, screen_height))

            # 监测到再来一场按钮，但是不打算开下一把时
            if next_game_flag:
                areaposition.click_next_game()
                return
            else:
                # 停止监控循环
                loop_running = False
                # 停止主循环
                main_running = False
                return

        # 1. 检测到排位场按钮时，触发点击
        if areaposition.check_ranking():
            areaposition.click_ranking()
            return  # 执行后跳过后续检查，避免冲突

        # 1. 检测到银之间按钮时，触发点击
        if areaposition.check_silver_room():
            areaposition.click_silver_room()
            return  # 执行后跳过后续检查，避免冲突

        # 1. 检测到四人东按钮时，触发点击
        if areaposition.check_siren_dong():
            areaposition.click_siren_dong()
            return  # 执行后跳过后续检查，避免冲突

        # 1. 检测到小局确认按钮时，触发点击
        if areaposition.check_confirm():
            areaposition.click_confirm()
            return  # 执行后跳过后续检查，避免冲突

        # 1. 检测到终局确认按钮时，触发点击
        if areaposition.check_end_confirm():
            screen_width, screen_height = pyautogui.size()
            # 截取屏幕（会自动保存）
            # screen_img = mjtools.capture_save(AreaPosition("", 0, screen_width, 0, screen_height))
            areaposition.click_end_confirm()
            return  # 执行后跳过后续检查，避免冲突

        # 1. 检测到获取奖励按钮时，触发点击
        if areaposition.check_get_reward():
            areaposition.click_get_reward()
            return  # 执行后跳过后续检查，避免冲突

        # 3. 检测到和off按钮时，触发点击和按钮
        if areaposition.check_he_off():
            areaposition.click_he()
            return

        # 4. 检测到鸣off按钮时，触发点击鸣按钮
        if areaposition.check_ming_off():
            areaposition.click_ming()
            return

        # if areaposition.check_

        # 监测到为自己回合
        if areaposition.check_my_turn():
            # 监测到跳过按钮
            if areaposition.check_skip():
                # 7-9. 立直按钮位置检查
                for pos in [2, 3]:
                    if areaposition.check_li_zhi(pos):
                        # 把鼠标移动到自由位置
                        mjtools.move_to_free_area()

                        areaposition.click_pos_button(pos)
                        time.sleep(0.1 * random.randint(1, 5))
                        # 找到第一个可以用于立直的牌
                        pos = areaposition.find_first_card_available()
                        # 点击该位置的牌
                        areaposition.move_to_and_click_card_at_pos(pos)
                        # time.sleep(0.1 * random.randint(1, 4))

                        # 5. 检测到切off按钮时，触发点击切按钮
                        if areaposition.check_qie_off():
                            areaposition.click_qie()

                        return

                # 10-12. 和按钮位置检查
                for pos in [2, 3, 4]:
                    if areaposition.check_he(pos):
                        areaposition.click_pos_button(pos)
                        return

                # 13-15. 自摸按钮位置检查
                for pos in [2, 3, 4]:
                    if areaposition.check_zi_mo(pos):
                        areaposition.click_pos_button(pos)
                        return

                # 16. 没检测到立直、和、自摸按钮时，点击第一个按钮跳过
                areaposition.click_pos_button(1)
            else:
                # 没监测到跳过按钮，就是自己回合的出牌时间
                # 把鼠标移动到自由位置，便于识别手牌
                mjtools.move_to_free_area()
                # 获取手牌
                areaposition.get_cards()
                # 判断当前应该丢弃的牌
                discard = calculate_discard.calculate_discard()
                # discard = model.predicted()
                # 把结果赋值给card_position[0].name
                areaposition.card_position[0].name = discard
                # 保存手牌和丢弃的牌到excel
                # areaposition.write_cards_to_excel()
                # 点击要丢弃的牌
                areaposition.move_to_and_click_card(discard)
                # 点击随机卡牌
                # areaposition.move_to_and_click_random_card()
                return
        else:
            # 不是自己的回合却监测到跳过按钮（吃碰杠）
            if areaposition.check_skip():
                # 点击第一个跳过按钮
                areaposition.click_pos_button(1)
                return


def close_app(app_name):
    # 遍历所有进程（不预加载info，避免属性问题）
    for proc in psutil.process_iter():
        try:
            # 直接获取进程名和PID（加try避免无权限访问的进程报错）
            proc_name = proc.name()
            proc_pid = proc.pid
            # 忽略大小写匹配进程名
            if app_name.lower() in proc_name.lower():
                proc.terminate()  # 正常终止，不行换 proc.kill()
                print(f"已关闭 {proc_name}（PID：{proc_pid}）")
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            # 跳过已结束的进程/无权限访问的系统进程
            continue


def on_key_press(key):
    """键盘事件回调：检测 Ctrl+P 按下"""
    global loop_running, Exit_flag
    print(f"\n检测到 按键按下\n{key}")
    try:
        # 检查是否按下 I
        if (
                key == keyboard.KeyCode.from_char('i')
                # and
                # (keyboard.Controller().pressed(keyboard.Key.ctrl_l) or
                #  keyboard.Controller().pressed(keyboard.Key.ctrl_r))
        ):
            # 下一场游戏不会开始
            global next_game_flag
            next_game_flag = False
            Exit_flag = True
            print("\n检测到 I，准备停止下一场游戏...")
        # 检查是否按下 P（pynput中，Key.ctrl_l/ctrl_r 表示左右Ctrl）
        if ((
                key == keyboard.KeyCode.from_char('p')
                # and
                # (keyboard.Controller().pressed(keyboard.Key.ctrl_l) or
                #  keyboard.Controller().pressed(keyboard.Key.ctrl_r))
        ) or
                key == '\x10'):
            # 暂停监控循环
            loop_running = False
            print("\n检测到 P，准备停止监控...")
        # 检查是否按下 O
        if ((
                key == keyboard.KeyCode.from_char('o')
        ) or
                key == '\x0f'):
            # 重新打开监控循环
            monitor.start()
            # 用于将手牌存到excel
            # areaposition.get_cards()
            # areaposition.write_cards_to_excel()
            print("\n检测到 o，开始监控...")
            # return False  # 停止监听键盘

        # 检查是否按下 U
        if ((
                key == keyboard.KeyCode.from_char('u')
        ) or
                key == keyboard.KeyCode.from_char('U')):
            global delay_exit_time
            if delay_exit_time is None:  # 避免重复触发
                delay_exit_time = time.time()  # 记录当前时间（秒级时间戳）
                Exit_flag = True
                print("检测到u键，将在3小时后自动停止监控...")
            else:
                print("已设置过延迟退出，无需重复操作")
    except AttributeError:
        pass  # 忽略特殊键（如Ctrl本身）


# # 启动键盘监听线程
# def start_keyboard_listener():
#     listener = keyboard.Listener(on_press=on_key_press)
#     listener.start()
#     return listener
#
#
# # 停止键盘监听线程（释放资源）
# def stop_keyboard_listener():
#     global keyboard_listener
#     if keyboard_listener is not None and keyboard_listener.is_alive():
#         keyboard_listener.stop()
#         keyboard_listener.join()  # 等待线程结束
#     keyboard_listener = None


def main():
    # 检查参考图片是否存在
    reference_path = "referenceImg/eat_3.jpg"
    if not os.path.exists(reference_path):
        print("错误：同目录下未找到1.jpg文件")
        return
    reference_path2 = "eat_2.jpg"
    if not os.path.exists(reference_path):
        print("错误：同目录下未找到1.jpg文件")
        return
    # 加载参考图片
    reference_img = cv2.imread(reference_path)
    reference_img2 = cv2.imread(reference_path2)
    # 确保参考图片尺寸正确
    # if reference_img.shape[:2] != (100, 100):
    #     print("错误：1.jpg的尺寸必须是100x100像素")
    #     return

    new_area = AreaPosition("和", 15, 65, 590, 640)

    # 截取屏幕（会自动保存）
    screen_img = mjtools.capture(new_area)

    # 比较相似度
    similarity = mjtools.compare_image_in_larger_image(reference_img2, reference_img)
    print(f"图片相似度: {similarity:.2f}%")

    # 如果相似度大于90%，点击屏幕中央
    if similarity > 90:
        print("相似度超过90%，执行点击操作")
        screen_width, screen_height = pyautogui.size()
        center_x, center_y = screen_width // 2, screen_height // 2

        # 移动鼠标到中央并点击
        pyautogui.moveTo(center_x, center_y, duration=0.2)  # 0.2秒内移动过去
        pyautogui.click()


if __name__ == "__main__":
    # 启动键盘监听（单独线程，不阻塞监控循环）
    keyboard_listener = keyboard.Listener(on_press=on_key_press)
    keyboard_listener.start()

    # 保持主程序运行
    # while True:
    #     time.sleep(100)

    # 创建监控器实例，设置检查间隔为0.5秒
    monitor = Monitor(interval=0.5)
    monitor.start()

    try:
        # 保持主程序运行
        while main_running:
            time.sleep(1)
    except KeyboardInterrupt:
        # 捕获Ctrl+C停止监控
        monitor.stop()

    if Exit_flag:
        time.sleep(5)
        close_app("Jantama_MahjongSoul.exe")

    # 用于测试展示相似度
    # pos = areaposition.find_first_card_available()
    # print(pos)

    # # 用于测试和参考图片的相似程度
    # # 检查参考图片是否存在
    # reference_path1 = "referenceImg/he_off.jpg"
    # reference_path2 = "referenceImg/he_on.jpg"
    #
    # # 加载参考图片
    # reference_img1 = cv2.imread(reference_path1)
    # reference_img2 = cv2.imread(reference_path2)
    #
    # 截取屏幕（会自动保存）
    # screen_img = mjtools.capture_save(areaposition.siren_dong_position)
    #
    # # 比较相似度
    # similarity = mjtools.compare_images_with_color(reference_img1, screen_img)
    # print(f"图片相似度: {similarity:.2f}%")
    # similarity = mjtools.compare_images_with_color(reference_img2, screen_img)
    # print(f"图片相似度: {similarity:.2f}%")

    # 用于将手牌存到excel
    # areaposition.get_cards()
    # areaposition.write_cards_to_excel()

    # 用于测试某个按钮的check和click
    # if areaposition.check_qie_off():
    #     areaposition.click_end_confirm()
    # areaposition.check_my_turn()
    # 用于测试截取屏幕位置（会自动保存）
    # screen_img = mjtools.capture(areaposition.card_available_position[11])
